require 'digest/sha1'

class User < ActiveRecord::Base
  include Authentication
  include Authentication::ByPassword
  include Authentication::ByCookieToken

  has_many :periods,
           :order => 'begin_at'

  belongs_to :team

  validates_presence_of     :login,
                            :message => 'ログイン名が必要です。'
  validates_length_of       :login,    :within => 1..40,
                            :too_short => '1文字以上のログイン名が必要です。',
                            :too_long => '40文字以内でログイン名を指定してください。'
  validates_uniqueness_of   :login,
                            :message => '既に使用されているログイン名です。'
  validates_format_of       :login,    :with => Authentication.login_regex,
                            :message => 'ログイン名に使える文字は半角英数字と .-_@ です。'

  validates_presence_of     :name,
                            :message => '名前が必要です。'
  validates_format_of       :name,     :with => Authentication.name_regex,
                            :message => '不正な名前です。'
  validates_length_of       :name,     :within => 1..100,
                            :too_short => '1文字以上の名前が必要です。',
                            :too_long => '100文字以内で名前を指定してください。'

  validates_inclusion_of    :permission, :in => %w{ admin editor viewer },
                            :message => '権限が不正です。'

  # HACK HACK HACK -- how to do attr_accessible from here?
  # prevents a user from submitting a crafted form that bypasses activation
  # anything else you want your user to change should be added here.
  attr_accessible :login, :name, :password, :password_confirmation, :permission, :team_name, :before_displayed_user_name

  attr_writer :team_name, :before_displayed_user_name

  def team_name
    has_team? ? self.team.name : (@team_name || '')
  end

  def validate
    if !@team_name.nil?
      self.team_id = nil
      if !@team_name.blank?
        begin
          self.team = Team.find_or_create_by_name(@team_name)
          if self.team.new_record?
            self.team = nil
            raise
          end
          @team_name = nil
        rescue
          errors.add(:team_name, "チーム名が不正です")
        end
      end
    end

    if !@before_displayed_user_name.nil?
      begin
        if @before_displayed_user_name.blank?
          if self.display_order != 1
            if self.class.find_by_display_order(1)
              self.class.update_all('display_order = display_order + 1')
            end
            self.display_order = 1
          end
        else
          tail = self.class.find_by_name(@before_displayed_user_name) or raise
          if self.display_order != tail.display_order + 1
            if self.class.find_by_display_order(tail.display_order + 1)
              self.class.update_all('display_order = display_order + 1',
                                    ['display_order > ?', tail.display_order])
            end
            self.display_order = tail.display_order + 1
          end
        end
      rescue
        errors.add(:before_displayed_user_name, "並び順が不正です")
      end
    elsif self.display_order.nil?
      self.display_order = self.class.next_display_order
    end
  end

  # Authenticates a user by their login name and unencrypted password.  Returns the user or nil.
  #
  # uff.  this is really an authorization, not authentication routine.  
  # We really need a Dispatch Chain here or something.
  # This will also let us return a human error message.
  #
  def self.authenticate(login, password)
    return nil if login.blank? || password.blank?
    u = find_by_login(login) # need to get the salt
    u && u.authenticated?(password) ? u : nil
  end

  def login=(value)
    write_attribute :login, (value ? value.downcase : nil)
  end

  def admin?
    self.permission == 'admin'
  end
  def editor?
    self.permission == 'admin' || self.permission == 'editor'
  end
  def viewer?
    self.permission == 'admin' || self.permission == 'editor' || self.permission == 'viewer'
  end

  def permission_as_japanese
    case permission
    when 'admin'
      '管理'
    when 'editor'
      'タスク編集'
    when 'viewer'
      '閲覧のみ'
    else
      ''
    end
  end

  def self.multiple_admins_exists?
    self.count(:conditions => { :permission => 'admin' }) > 1
  end

  def only_admin?
    admin? && !self.class.multiple_admins_exists?
  end

  def has_team?
    !self.team_id.nil? && !!self.team
  end


  def before_displayed_user
    self.new_record? ? self.class.last_displayed_user : self.class.find(
      :first,
      :conditions => ['display_order < ?', self.display_order],
      :order => 'display_order desc'
    )
  end

  def before_displayed_user_name
    user = before_displayed_user
    user ? user.name : nil
  end

  def self.last_displayed_user
    self.find(:first, :order => 'display_order desc')
  end

  def self.next_display_order
    tail = last_displayed_user
    tail ? tail.display_order + 1 : 1
  end

end
